#include "widgets/plotting/PlotWidget.h"
#include "widgets/plotting/PlotPicker.h"
#include "widgets/plotting/PlotMagnifier.h"
#include "widgets/plotting/PlotZoomer.h"
#include "widgets/plotting/PlotPanner.h"

#include <qwt_symbol.h>
#include <qwt_scale_widget.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_renderer.h>
#include <qwt_scale_map.h>

#include <QtGui/QMenu>
#include <QtCore/QString>
#include <QtGui/QKeyEvent>
#include <QtGui/QFileDialog>
#include <QtGui/QInputDialog>
#include <QtGui/QPrinter>
#include <QtGui/QMessageBox>

#include <iostream>
#include <iomanip>
#include <cmath>
#include <vector>
using namespace std;

namespace spectrum {

/**
 * @details
 */
PlotWidget::PlotWidget(QWidget* parent)
: QwtPlot(parent), _adjustingColours(false), _oldx(0), _oldy(0),
  _picker(canvas()), _panner(canvas()),
  _zoomer(canvas()), _magnifier(canvas()), _scale(axisWidget(QwtPlot::yRight))
{
    // Setup the colour map.
    _colourMap.set(ColourMap::RAINBOW);

    // Setup the plot grid.
    _setGrid();

    // Setup the plot panner.
    _setPanner();

    // Setup the plot zoomer.
    _setZoomer();

    // Connect signals to slots.
    _connectSignalsAndSlots();

    // Clear and initialise the widget.
    clear();

    if (!isVisible())
        setVisible(true);

    // what does this do? - its from the spectrogram example...
//    plotLayout()->setAlignCanvasToScales(true);

    // Settings for stand alone mode (i.e not embedded in a parent widget).
    if (parent == 0)
    {
        setMinimumSize(500, 500);
        showGrid(false);
        setCanvasBackground(QColor(Qt::white));
    }
}


/**
 * @details
 */
PlotWidget::~PlotWidget()
{
}


/**
 * @details
 */
void PlotWidget::slotSelectedPoint(const QPoint& /* point */)
{
}


///**
// * @details
// */
//void PlotWidget::slotSelectedRect(const QwtDoubleRect& rectangle)
//{
//    qreal xTopLeft, yTopLeft, xBottomRight, yBottomRight;
//    rectangle.getCoords(&xTopLeft, &yTopLeft, &xBottomRight, &yBottomRight);
//    emit xLeftChanged(xTopLeft);
//    emit xRightChanged(xBottomRight);
//    emit yTopChanged(yTopLeft);
//    emit yBottomChanged(yBottomRight);
//}


/**
 * @details
 * Clears the plot widget.
 */
void PlotWidget::clear()
{
    setAxisScale(QwtPlot::yLeft, -1.0, 1.0);
    setAxisScale(QwtPlot::xBottom, -1.0, 1.0);
    enableAxis(QwtPlot::yRight, false);
    plotLayout()->setAlignCanvasToScales(true);
    setTitle("");
    setXLabel("");
    setYLabel("");
    updateZoomBase();
    showGrid(true);
    replot();
}


/**
 * @details
 */
void PlotWidget::savePNG(QString fileName, unsigned sizeX, unsigned sizeY)
{
    if (fileName.isEmpty())
    {
        fileName = QFileDialog::getSaveFileName(this,
                "Save plot widget as PNG: file name", QString(), "(*.png)");
    }

    if (fileName.isEmpty()) return;

    QFileInfo fileInfo(fileName);
    if (fileInfo.suffix() != "png")
        fileName += ".png";

    QPixmap pixmap(sizeX, sizeY);
    pixmap.fill();

//    print(pixmap);

    QwtPlotRenderer renderer;
    renderer.setDiscardFlag(QwtPlotRenderer::DiscardBackground, false);
    renderer.setLayoutFlag(QwtPlotRenderer::KeepFrames, true);
    renderer.renderTo(this, pixmap);

    int quality = -1; // -1 = default, [0..100] otherwise.
    if (!pixmap.save(fileName, "PNG", quality))
        throw QString("PlotWidget::exportPNG(): Error saving PNG");
}


/**
 *
 * @param fileName
 */
void PlotWidget::savePDF(QString fileName, unsigned sizeX, unsigned sizeY)
{
    if (fileName.isEmpty())
    {
        fileName = QFileDialog::getSaveFileName(this,
                "Save plot widget as PDF: file name", QString(), "(*.pdf)");
    }

    if (fileName.isEmpty()) return;

    QFileInfo fileInfo(fileName);
    if (fileInfo.suffix() != "pdf")
        fileName += ".pdf";

    QPrinter printer(QPrinter::ScreenResolution);
    printer.setDocName(fileName);
    printer.setOutputFileName(fileName);
    printer.setCreator("oskar");
    printer.setColorMode(QPrinter::Color);
    printer.setOutputFormat(QPrinter::PdfFormat);
    printer.setPaperSize(QSizeF(sizeX, sizeY), QPrinter::Point);

    QwtPlotRenderer renderer;

    renderer.setDiscardFlag(QwtPlotRenderer::DiscardBackground, false);
    renderer.setLayoutFlag(QwtPlotRenderer::KeepFrames, true);

    renderer.renderTo(this, printer);

    //print(printer);
}




/**
 * @details
 */
void PlotWidget::rectangleSelect(bool enable)
{
    if (enable)
        _picker.setSelectionType(PlotPicker::RECT_SELECT);
    else
        _picker.setSelectionType(PlotPicker::POINT_SELECT);
}




/**
 * @details
 */
void PlotWidget::updateZoomBase()
{
    _zoomer.setZoomBase(true);
}


/**
 * @details
 */
void PlotWidget::menuSetTitle()
{
    bool ok = false;
    QString text = QInputDialog::getText(this, "Set Plot Title", "Title: ",
            QLineEdit::Normal, title().text(), &ok);

    if (ok && text.isEmpty()) setTitle("");
    if (ok && !text.isEmpty()) setTitle(text);
}


/**
 * @details
 */
void PlotWidget::setPlotTitle(const QString& text)
{
    QFont font;
    font.setPointSize(11);
    QwtText label(text);
    label.setFont(font);
    setTitle(label);
    emit titleChanged(text);
}


/**
 * @details
 */
void PlotWidget::setXLabel(const QString& text)
{
    QFont font;
    font.setPointSize(9);
    QwtText label(text);
    label.setFont(font);
    setAxisTitle(QwtPlot::xBottom, label);
    emit xLabelChanged(text);
}


/**
 * @details
 */
void PlotWidget::menuSetXLabel()
{
    bool ok = false;
    QString t = QInputDialog::getText(this, "Set X Label", "Label: ",
            QLineEdit::Normal, axisTitle(QwtPlot::xBottom).text(), &ok);
    if (ok && t.isEmpty()) setAxisTitle(QwtPlot::xBottom, "");
    if (ok && !t.isEmpty()) setXLabel(t);
}

/**
 * @details
 */
void PlotWidget::menuSetYLabel()
{
    bool ok = false;
    QString t = QInputDialog::getText(this, "Set Y Label", "Label: ",
            QLineEdit::Normal, axisTitle(QwtPlot::yLeft).text(), &ok);

    if (ok && t.isEmpty()) setAxisTitle(QwtPlot::yLeft, "");
    else if (ok && !t.isEmpty()) setYLabel(t);
}


/**
 * @details
 */
void PlotWidget::setYLabel(const QString& text)
{
    QFont font;
    font.setPointSize(9);
    QwtText label(text);
    label.setFont(font);
    setAxisTitle(QwtPlot::yLeft, label);
    emit yLabelChanged(text);
}


/**
 *
 * @param text
 */
void PlotWidget::setScaleLabel(const QString& text)
{
    QFont scaleFont;
    scaleFont.setPointSize(9);
    QwtText scaleLabel(text);
    scaleLabel.setFont(scaleFont);
    _scale.setTitle(scaleLabel);
}


/**
 * @details
 */
void PlotWidget::setAxesVisible(int state)
{
    bool visible = (state == Qt::Checked) ? true : false;
    enableAxis(QwtPlot::xBottom, visible);
    enableAxis(QwtPlot::yLeft, visible);
}


/**
 * @details
 *
 * @param on
 */
void PlotWidget::showGrid(bool on)
{
    _grid.setVisible(on);
    replot();
    emit gridEnabled(on);
}




/**
 * @details
 */
void PlotWidget::showGridMinorTicks(bool on)
{
    _grid.enableXMin(on);
    _grid.enableYMin(on);
    replot();
    emit gridMinorTicks(on);
}

void PlotWidget::toggleGrid()
{
    // Toggle the grid visibility.
    _grid.setVisible(!_grid.isVisible());

    // Re-draw.
    replot();

    // Emit the grid visibility signal.
    emit gridEnabled(_grid.isVisible());
}


void PlotWidget::toggleGridMinorTicks()
{
    // Don't do anything if the grid is disabled.
    if (!_grid.isVisible()) return;

    // Toggle minor grid ticks.
    _grid.enableXMin(!_grid.xMinEnabled());
    _grid.enableYMin(!_grid.yMinEnabled());

    replot();

    emit gridMinorTicks(_grid.xMinEnabled());
}



/**
 * @details
 */
void PlotWidget::enableColourScale(bool on)
{
    QList<QwtPlotItem*> items = itemList();
//    if (!items.contains(&_image)) return;

    if (on) {
//        _image.setColorMap(_colourMap.map());
//        _scale->setColorMap(_image.ampRange(), _colourMap.map());
        _scale.setColorBarEnabled(true);
        enableAxis(QwtPlot::yRight, true);
    }
    else {
        _scale.setColorBarEnabled(false);
        enableAxis(QwtPlot::yRight, false);
    }
}


void PlotWidget::togglePanner(bool on)
{
    _panner.setEnabled(on);
}


void PlotWidget::toggleZoomer(bool on)
{
    _zoomer.setEnabled(on);
}


void PlotWidget::togglePicker(bool on)
{
    _picker.setEnabled(on);
}

/**
 * @details
 * Process the mouse press event.
 */
void PlotWidget::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::RightButton && event->modifiers() == Qt::ShiftModifier)
    {
        _oldx = event->x();
        _oldy = event->y();
        _adjustingColours = true;
    }
}


/**
 * @details
 * Process the mouse move event. If adjusting colours (set by a mouse
 * press event) the colour map is updated.
 *
 * @param[in] event Qt Mouse event object pointer.
 */
void PlotWidget::mouseMoveEvent(QMouseEvent* event)
{
    float sensitivity = 800.0f;

    if (_adjustingColours)
    {
        int x = event->x();
        int y = event->y();
        float c = _colourMap.contrast() + float(x - _oldx) / sensitivity;
        float b = _colourMap.brightness() + float(y - _oldy) / sensitivity;
        _oldx = x;
        _oldy = y;
        _colourMap.update(b, c);

//        _image.setColorMap(_colourMap.map());
        enableColourScale();
        replot();
    }
}


/**
 * @details
 * Process the mouse release event.
 */
void PlotWidget::mouseReleaseEvent(QMouseEvent* event)
{
    if (event->button() == Qt::RightButton && _adjustingColours)
        _adjustingColours = false;
}


/**
 *
 * @param
 */
void PlotWidget::keyPressEvent(QKeyEvent* event)
{
    // Keys with shift modifier.
    if (event->modifiers() == Qt::ShiftModifier)
    {
        if (event->key() == Qt::Key_G)
            toggleGridMinorTicks();

        if (event->key() == Qt::Key_P)
            savePDF();
    }

    else if (event->modifiers() == Qt::AltModifier)
    {
        if (event->key() == Qt::Key_C)
            clear();
    }

    else if (event->modifiers() == Qt::ControlModifier)
    {
        if (event->key() == Qt::Key_C)
        {
            if (parentWidget() == 0)
                close();
        }
        else if (event->key() == Qt::Key_X)
            close();

    }
    // Unmodified keys.
    else if (event->modifiers() == Qt::NoModifier)
    {
        if (event->key() == Qt::Key_G)
            toggleGrid();

        else if (event->key() == Qt::Key_F5)
        {
            _colourMap.update();
            _oldx = 0;
            _oldy = 0;
            _zoomer.zoom(_zoomer.zoomBase());
            //_magnifier->rescale(1.0);
            replot();
        }

        else if (event->key() == Qt::Key_T)
            menuSetTitle();

        else if (event->key() == Qt::Key_X)
            menuSetXLabel();

        else if (event->key() == Qt::Key_Y)
            menuSetYLabel();

        else if (event->key() == Qt::Key_P)
            savePNG();

        else if (event->key() == Qt::Key_Escape)
        {
            if (parentWidget() == 0)
                close();
        }

        else if (event->key() == Qt::Key_F1)
        {
            QString h;
            h += "Keys:\n";
            h += "========================\n";
            h += "t\t Set the plot title.\n";
            h += "x\t Set the plot x label.\n";
            h += "y\t Set the plot y label.\n";
            h += "\n";
            h +=  "g\t Toggle grid.\n";
            h += "[shift] + g\t Toggle grid minor ticks.\n";
            h += "\n";
            h += "p\t Save the plot as a PNG.\n";
            h += "[shift] + p\t Save the plot as a PDF.\n";
            h += "\n";
            h += "F5\t Refresh the plot.\n";
            h += "\n";
            h += ".\tZoom in one step.\n";
            h += "/\tZoom out one step.\n";
            h += "\n";
            h += "w\tPan up.\n";
            h += "a\tPan left.\n";
            h += "s\tPan down.\n";
            h += "d\tPan right.\n";
            h += "\n";
            h += "q\tReset the plot (zoom/pan etc.).\n";
            h += "\n";
            h += "Esc\tClose the widget (safer - only works if no parent).\n";
            h += "[ctrl] + c\tClose the plot (same as above).\n";
            h += "\n";
            h += "[alt] + c\tClear the plot (use with care!).\n";
            h += "[ctrl] + x\tClose the widget (use with care!).\n";
            h += "\n";
            h += "Mouse:\n";
            h += "========================\n";
            h += "left-click\t\tZoom rectangle.\n";
            h += "right-click\t\tZoom out one step.\n";
            h += "[ctrl] + right-click\tZoom out fully.\n";
            h += "[shift] + right-click\tDisplay cross-hair position.\n";
            h += "[shift] + right-drag\tAdjust colour map.\n";
            h += "middle-drag\t\tPan plot.\n";
            h += "[shift] + wheel\tMagnify plot.\n";

            QMessageBox::information(this, "Key-bindings help", h);
        }
    }
}


/**
 * @details
 */
void PlotWidget::_setGrid()
{
    _grid.attach(this);

    _grid.setMinPen(QPen(QBrush(QColor(Qt::lightGray)), qreal(0.0), Qt::DotLine));
    _grid.setMajPen(QPen(QBrush(QColor(Qt::darkGray)),  qreal(0.0), Qt::DotLine));

    _grid.enableXMin(false);
    _grid.enableYMin(false);
}


/**
 * @details
 */
void PlotWidget::_setPanner()
{
    _panner.setAxisEnabled(QwtPlot::yRight, false);
}


/**
 * @details
 */
void PlotWidget::_setZoomer()
{
    _magnifier.setAxisEnabled(QwtPlot::yRight, false);
}


/**
 * @details
 * Sets signal and slot connections.
 */
void PlotWidget::_connectSignalsAndSlots()
{
    // Signals emitted by the plot picker.
    // =========================================================================
//    connect(_picker, SIGNAL(selected(const QPoint &)),
//            this, SLOT(slotSelectedPoint(const QPoint &)));
//    connect(_picker, SIGNAL(selected(const QwtDoubleRect &)),
//            this, SLOT(slotSelectedRect(const QwtDoubleRect &)));

    // Signals emitted by the colour map.
    // =========================================================================
    connect(&_colourMap, SIGNAL(mapUpdated()),
            this, SLOT(enableColourScale()));
}

} // namespace spectrum
